// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/cast/net/rtcp/receiver_rtcp_event_subscriber.h"

#include <algorithm>
#include <utility>

namespace media {
namespace cast {

    ReceiverRtcpEventSubscriber::ReceiverRtcpEventSubscriber(
        const size_t max_size_to_retain, EventMediaType type)
        : max_size_to_retain_(
            max_size_to_retain * (kResendDelay * kNumResends + 1))
        , type_(type)
        , popped_events_(0)
    {
        DCHECK(max_size_to_retain_ > 0u);
        DCHECK(type_ == AUDIO_EVENT || type_ == VIDEO_EVENT);
        for (size_t i = 0; i < kNumResends; i++) {
            send_ptrs_[i] = 0;
        }
    }

    ReceiverRtcpEventSubscriber::~ReceiverRtcpEventSubscriber()
    {
        DCHECK(thread_checker_.CalledOnValidThread());
    }

    void ReceiverRtcpEventSubscriber::OnReceiveFrameEvent(
        const FrameEvent& frame_event)
    {
        DCHECK(thread_checker_.CalledOnValidThread());

        if (ShouldProcessEvent(frame_event.type, frame_event.media_type)) {
            RtcpEvent rtcp_event;
            switch (frame_event.type) {
            case FRAME_PLAYOUT:
                rtcp_event.delay_delta = frame_event.delay_delta;
            case FRAME_ACK_SENT:
            case FRAME_DECODED:
                rtcp_event.type = frame_event.type;
                rtcp_event.timestamp = frame_event.timestamp;
                rtcp_events_.push_back(
                    std::make_pair(frame_event.rtp_timestamp, rtcp_event));
                break;
            default:
                break;
            }
        }

        TruncateMapIfNeeded();
    }

    void ReceiverRtcpEventSubscriber::OnReceivePacketEvent(
        const PacketEvent& packet_event)
    {
        DCHECK(thread_checker_.CalledOnValidThread());

        if (ShouldProcessEvent(packet_event.type, packet_event.media_type)) {
            RtcpEvent rtcp_event;
            if (packet_event.type == PACKET_RECEIVED) {
                rtcp_event.type = packet_event.type;
                rtcp_event.timestamp = packet_event.timestamp;
                rtcp_event.packet_id = packet_event.packet_id;
                rtcp_events_.push_back(
                    std::make_pair(packet_event.rtp_timestamp, rtcp_event));
            }
        }

        TruncateMapIfNeeded();
    }

    struct CompareByFirst {
        bool operator()(const std::pair<RtpTimeTicks, RtcpEvent>& a,
            const std::pair<RtpTimeTicks, RtcpEvent>& b)
        {
            return a.first < b.first;
        }
    };

    void ReceiverRtcpEventSubscriber::GetRtcpEventsWithRedundancy(
        RtcpEvents* rtcp_events)
    {
        DCHECK(thread_checker_.CalledOnValidThread());
        DCHECK(rtcp_events);

        uint64_t event_level = rtcp_events_.size() + popped_events_;
        event_levels_for_past_frames_.push_back(event_level);

        for (size_t i = 0; i < kNumResends; i++) {
            size_t resend_delay = kResendDelay * i;
            if (event_levels_for_past_frames_.size() < resend_delay + 1)
                break;

            uint64_t send_limit = event_levels_for_past_frames_[event_levels_for_past_frames_.size() - 1 - resend_delay];

            if (send_ptrs_[i] < popped_events_) {
                send_ptrs_[i] = popped_events_;
            }

            while (send_ptrs_[i] < send_limit && rtcp_events->size() < kMaxEventsPerRTCP) {
                rtcp_events->push_back(rtcp_events_[send_ptrs_[i] - popped_events_]);
                send_ptrs_[i]++;
            }
            send_limit = send_ptrs_[i];
        }

        if (event_levels_for_past_frames_.size() > kResendDelay * (kNumResends + 1)) {
            while (popped_events_ < event_levels_for_past_frames_[0]) {
                rtcp_events_.pop_front();
                popped_events_++;
            }
            event_levels_for_past_frames_.pop_front();
        }

        std::sort(rtcp_events->begin(), rtcp_events->end(), CompareByFirst());
    }

    void ReceiverRtcpEventSubscriber::TruncateMapIfNeeded()
    {
        // If map size has exceeded |max_size_to_retain_|, remove entry with
        // the smallest RTP timestamp.
        if (rtcp_events_.size() > max_size_to_retain_) {
            DVLOG(3) << "RTCP event map exceeded size limit; "
                     << "removing oldest entry";
            // This is fine since we only insert elements one at a time.
            rtcp_events_.pop_front();
            popped_events_++;
        }

        DCHECK(rtcp_events_.size() <= max_size_to_retain_);
    }

    bool ReceiverRtcpEventSubscriber::ShouldProcessEvent(
        CastLoggingEvent event_type, EventMediaType event_media_type)
    {
        return type_ == event_media_type && (event_type == FRAME_ACK_SENT || event_type == FRAME_DECODED || event_type == FRAME_PLAYOUT || event_type == PACKET_RECEIVED);
    }

} // namespace cast
} // namespace media
